#ifndef MEMORY_HANDLER_H
#define MEMORY_HANDLER_H

#include "common.h"

typedef struct {
	unsigned char * block_data;
	int valid;
	int tag;
	int dirty;
} CacheBlock;

typedef struct {
	CacheBlock * cache_blocks;
} CacheWay;

typedef struct Cache {
	char name[32]; //used for debug printouts
	int policy; //write policy of the cache - WRITE-TROUGH or WRITE-BACK

	CacheWay * cache_ways;
	int * LRU; // Implementing LRU for 2-ways cache. Generally, the code may support any number of ways, if we hold LRU for every block

	int num_of_ways;
	int num_of_blocks_per_way;
	int block_size;
	int num_words_in_block;

	int access_delay;
	int accesses;
	int misses;

	struct Cache * nextLevelCache; //pointer to upper level cache. If NULL than next level is main memory.

	int num_offset_bits;
	int num_index_bits;
	int num_tag_bits;

	int offset_mask; //used to isolate the offset from the address
	int tag_mask; //used to isolate the tag from the address
	int index_mask; //used to isolate the index from the address

	// variables used for critical word first and write allocation
	int busy_cycles;
	int last_requested_address;
	int start_address;
	int end_address;
	int current_imported_address;

	int next_requested_address;


} Cache;

typedef struct {
	
	int address;
	
	int isInstruction;
	int isWrite;

	int L1_hit;
	int L2_hit;

	int wasDirty;

} CacheAccessInfo;


typedef struct {
	int busy_cycles;
	int wait_before_import;
} WriteBuffer;

enum CACHE_POLICIES_ENUM {WRITE_BACK, WRITE_THROUGH};

extern unsigned char memory[MAX_MEMORY_SIZE];
int memory_size;

Cache L1_data_cache;
Cache L1_inst_cache;
Cache L2_data_cache;
Cache L2_inst_cache;

WriteBuffer write_buffer;
CacheAccessInfo cache_access_info;


int resetAMAT(); //Init AMAT variables

int loadMemoryFile(char * file_path);
int printMemory(FILE * target);

int initializeCaches();
int freeCaches();

int fetchInstructionFromCache(int inst_index);
int fetchDataFromCache(unsigned char * target, int address);
int storeDataToCache(unsigned char * buffer, int address);

int printCache(Cache * cache, FILE * target);
int printHitRates(FILE * target);

#endif